// MainWorkerThread.cpp : implementation file
//

#include "stdafx.h"
#include "PublicDNSPluginService.h"
#include "MainWorkerThread.h"
#include "Ip4ElaborateThread.h"
#include "DnsResorceRecord.h"
#include "Trace.h"
#include "MainWorkerThread.tmh"

// CMainWorkerThread

IMPLEMENT_DYNCREATE(CMainWorkerThread, CWinThread)

CMainWorkerThread::CMainWorkerThread()
: m_nRetCode(0)
, m_hWfpEngineHandle(INVALID_HANDLE_VALUE)
, m_pDnsServerAppID(NULL)
, m_nStreamFilterID(0)
, m_nDatagramDataFilterID(0)
, m_nStreamCalloutId(0)
, m_nDatagramsCalloutId(0)
, m_nAleFlowEstablishedCalloutId(0)
, m_nAleFlowEstablishedV6CalloutId(0)
, m_nAleFlowEstablishedFilterID(0)
, m_bOnDelete(false)
, m_oThreadsSemaphore(ELABORATE_THREADS,ELABORATE_THREADS)
, m_hDnsFilterDevice(INVALID_HANDLE_VALUE)
, m_oDnsResponceEvent(FALSE,TRUE)
, m_bServiceTest(false)
, m_wXid(0)
, m_nStreamV6CalloutId(0)
, m_nDatagramsV6CalloutId(0)
, m_nAleFlowEstablishedV6FilterID(0)
, m_nStreamV6FilterID(0)
, m_nDatagramV6DataFilterID(0)
, m_nFilterWeight(0xFFFFFFFFFFFFFFFE)
{
    PSOCKADDR_IN    pSockAddres = (PSOCKADDR_IN)&m_stLocalDnsServerAddress;
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Enter");
    m_bAutoDelete = FALSE;
    m_oUdpLocalSocket.m_pOwnerThread = this;
    RtlZeroMemory(
        &m_stLocalDnsServerAddress,
        sizeof(m_stLocalDnsServerAddress)
        );
    pSockAddres->sin_family = AF_INET;
    pSockAddres->sin_port = htons(DNS_PORT);
    pSockAddres->sin_addr.S_un.S_addr = netAddress(127,0,0,1);
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Exit");
}

CMainWorkerThread::~CMainWorkerThread()
{
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Enter");
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Exit");
}

BOOL CMainWorkerThread::InitInstance()
{
    BOOL                    bRet;
    DWORD                   dwResult;
    FWPM_SESSION            stFwpmSession;
    FWPM_FILTER             stDnsFilter;
    FWPM_CALLOUT            stCallOut;
    FWPM_FILTER_CONDITION   staFilterConditions[16];
    int     nConditionIndex;
    int     nSockError;
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Enter");
    _tsetlocale(LC_ALL, _T("Russian"));
    bRet = CWinThread::InitInstance();
    bRet = AfxSocketInit( );
    if(!bRet)
    {
        m_nRetCode = GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! The subsystem failed to initialize \"Sockets\"."
            );
        m_pDnsServerAppID = NULL;
        return FALSE;
    }
    bRet = m_oUdpLocalSocket.Create(
        0,
        SOCK_DGRAM,
        FD_READ
        );
    if(!bRet)
    {
        nSockError = CAsyncSocket::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Failed to create object \"m_oUdpLocalSocket\". Error: %!WINERROR!",
            nSockError
            );
        m_nRetCode = nSockError;
        return FALSE;
    }
    dwResult = FwpmGetAppIdFromFileName(
        L"%systemroot%\\system32\\dns.exe",
        &m_pDnsServerAppID
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Failed to get the ID of the DNS server. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_pDnsServerAppID = NULL;
        return FALSE;
    }
    RtlZeroMemory(
        &stFwpmSession,
        sizeof(stFwpmSession)
        );
    stFwpmSession.displayData.name = L"DNS Filter Session";
    stFwpmSession.displayData.description = L"Public DNS Filter engine Session";
    stFwpmSession.flags = FWPM_SESSION_FLAG_DYNAMIC;
    dwResult = FwpmEngineOpen(
        NULL,
        RPC_C_AUTHN_WINNT,
        NULL,
        &stFwpmSession,
        &m_hWfpEngineHandle
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error opening WFP session. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_hWfpEngineHandle = INVALID_HANDLE_VALUE;
        return FALSE;
    }
    dwResult = FwpmTransactionBegin(
        m_hWfpEngineHandle,
        0
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Transaction failed to start creating a set of WFP-filters. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        return FALSE;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DNS_FILTER_ALE_FLOW_ESTABLISHED_CALLOUT_V4;
    stCallOut.displayData.name        = L"DNS filter ALE_FLOW_ESTABLISHED CALLOUT";
    stCallOut.displayData.description = L"DNS filter Flow Established with DNS-client CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nAleFlowEstablishedCalloutId
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error adding handler installation packet stream with a DNS client. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nAleFlowEstablishedCalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DNS_FILTER_ALE_FLOW_ESTABLISHED_CALLOUT_V6;
    stCallOut.displayData.name        = L"DNS filter ALE_FLOW_ESTABLISHED IPv6 CALLOUT";
    stCallOut.displayData.description = L"DNS filter Flow Established IPv6 with DNS-client CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nAleFlowEstablishedV6CalloutId
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error adding handler installation packet stream IPv6 with a DNS client. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nAleFlowEstablishedV6CalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DSN_FILTER_STREAM_CALLOUT_V4;
    stCallOut.displayData.name        = L"DNS filter TCPv4-stream Flow CALLOUT";
    stCallOut.displayData.description = L"DNS filter TCPv4-stream Flow CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_STREAM_V4;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nStreamCalloutId
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error adding a handler thread TCPv4 DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nStreamCalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DSN_FILTER_STREAM_CALLOUT_V6;
    stCallOut.displayData.name        = L"DNS filter TCPv6-stream Flow CALLOUT";
    stCallOut.displayData.description = L"DNS filter TCPv6-stream Flow CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_STREAM_V6;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nStreamV6CalloutId
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error adding a handler thread TCPv6 DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nStreamV6CalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DSN_FILTER_DATAGRAM_DATA_CALLOUT_V4;
    stCallOut.displayData.name        = L"DNS filter UDPv4-datagrams Flow CALLOUT";
    stCallOut.displayData.description = L"DNS filter UDPv4-datagrams Flow CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_DATAGRAM_DATA_V4;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nDatagramsCalloutId
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error adding a handler thread UDPv4 DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nDatagramsCalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stCallOut,
        sizeof(stCallOut)
        );
    stCallOut.calloutKey = DSN_FILTER_DATAGRAM_DATA_CALLOUT_V6;
    stCallOut.displayData.name        = L"DNS filter UDPv6-datagrams Flow CALLOUT";
    stCallOut.displayData.description = L"DNS filter UDPv6-datagrams Flow CALLOUT";
    stCallOut.applicableLayer = FWPM_LAYER_DATAGRAM_DATA_V6;
    dwResult = FwpmCalloutAdd(
        m_hWfpEngineHandle,
        &stCallOut,
        NULL,
        &m_nDatagramsV6CalloutId
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error adding a handler thread UDPv6 DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nDatagramsV6CalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey                = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4;
    stDnsFilter.displayData.name        = L"ALE Flow Established DNS-requests IPv4 layer Filter";
    stDnsFilter.displayData.description = L"ALE Flow Established DNS-requests IPv4 layer Filter";
    stDnsFilter.flags                   = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type             = FWP_UINT64;
    stDnsFilter.weight.uint64           = &m_nFilterWeight;
    stDnsFilter.action.type             = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey       = DNS_FILTER_ALE_FLOW_ESTABLISHED_CALLOUT_V4;
    stDnsFilter.filterCondition         = staFilterConditions;
    stDnsFilter.subLayerKey             = FWPM_SUBLAYER_UNIVERSAL;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey  = FWPM_CONDITION_ALE_APP_ID;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type     = FWP_BYTE_BLOB_TYPE;
    staFilterConditions[nConditionIndex].conditionValue.byteBlob = m_pDnsServerAppID;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey  = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type   = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nAleFlowEstablishedFilterID
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error creating filter installation flow IPv4 DNS-packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nDatagramsCalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey                = FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6;
    stDnsFilter.displayData.name        = L"ALE Flow Established DNS-requests IPv6 layer Filter";
    stDnsFilter.displayData.description = L"ALE Flow Established DNS-requests IPv6 layer Filter";
    stDnsFilter.flags                   = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type             = FWP_UINT64;
    stDnsFilter.weight.uint64           = &m_nFilterWeight;
    stDnsFilter.action.type             = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey       = DNS_FILTER_ALE_FLOW_ESTABLISHED_CALLOUT_V6;
    stDnsFilter.filterCondition         = staFilterConditions;
    stDnsFilter.subLayerKey             = FWPM_SUBLAYER_UNIVERSAL;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey  = FWPM_CONDITION_ALE_APP_ID;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type     = FWP_BYTE_BLOB_TYPE;
    staFilterConditions[nConditionIndex].conditionValue.byteBlob = m_pDnsServerAppID;
    nConditionIndex++;
    staFilterConditions[nConditionIndex].fieldKey  = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type   = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nAleFlowEstablishedV6FilterID
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error creating filter installation flow IPv6 DNS-packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nDatagramsV6CalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey                = FWPM_LAYER_STREAM_V4;
    stDnsFilter.displayData.name        = L"DNS-packets Stream IPv4 flow Filter";
    stDnsFilter.displayData.description = L"DNS-packets Stream IPv4 flow Filter";
    stDnsFilter.flags                   = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type             = FWP_UINT64;
    stDnsFilter.weight.uint64           = &m_nFilterWeight;
    stDnsFilter.action.type             = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey       = DSN_FILTER_STREAM_CALLOUT_V4;
    stDnsFilter.filterCondition         = staFilterConditions;
    stDnsFilter.subLayerKey             = FWPM_SUBLAYER_UNIVERSAL;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey  = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type   = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nStreamFilterID
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error creating filter flow TCP IPv4 DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nStreamCalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey                = FWPM_LAYER_STREAM_V6;
    stDnsFilter.displayData.name        = L"DNS-packets Stream IPv6 flow Filter";
    stDnsFilter.displayData.description = L"DNS-packets Stream IPv6 flow Filter";
    stDnsFilter.flags                   = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type             = FWP_UINT64;
    stDnsFilter.weight.uint64           = &m_nFilterWeight;
    stDnsFilter.action.type             = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey       = DSN_FILTER_STREAM_CALLOUT_V6;
    stDnsFilter.filterCondition         = staFilterConditions;
    stDnsFilter.subLayerKey             = FWPM_SUBLAYER_UNIVERSAL;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey  = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type   = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nStreamV6FilterID
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error creating filter flow TCP IPv6 DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nStreamV6CalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_DATAGRAM_DATA_V4;
    stDnsFilter.displayData.name        = L"DNS-packets Datagram IPv4 flow Filter";
    stDnsFilter.displayData.description = L"DNS-packets Datagram IPv4 flow Filter";
    stDnsFilter.flags                   = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type             = FWP_UINT64;
    stDnsFilter.weight.uint64           = &m_nFilterWeight;
    stDnsFilter.action.type         = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey   = DSN_FILTER_DATAGRAM_DATA_CALLOUT_V4;
    stDnsFilter.filterCondition     = staFilterConditions;
    stDnsFilter.subLayerKey         = FWPM_SUBLAYER_UNIVERSAL;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey  = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type   = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nDatagramDataFilterID
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error creating filter flow UDP IPv4 DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nDatagramsCalloutId = 0;
        goto abortWfpTransaction;
    }
    RtlZeroMemory(
        &stDnsFilter,
        sizeof(stDnsFilter)
        );
    stDnsFilter.layerKey = FWPM_LAYER_DATAGRAM_DATA_V6;
    stDnsFilter.displayData.name        = L"DNS-packets Datagram IPv6 flow Filter";
    stDnsFilter.displayData.description = L"DNS-packets Datagram IPv6 flow Filter";
    stDnsFilter.flags                   = FWPM_FILTER_FLAG_INDEXED;
    stDnsFilter.weight.type             = FWP_UINT64;
    stDnsFilter.weight.uint64           = &m_nFilterWeight;
    stDnsFilter.action.type         = FWP_ACTION_CALLOUT_TERMINATING;
    stDnsFilter.action.calloutKey   = DSN_FILTER_DATAGRAM_DATA_CALLOUT_V6;
    stDnsFilter.filterCondition     = staFilterConditions;
    stDnsFilter.subLayerKey         = FWPM_SUBLAYER_UNIVERSAL;
    RtlZeroMemory(
        staFilterConditions,
        sizeof(staFilterConditions)
        );
    nConditionIndex = 0;
    staFilterConditions[nConditionIndex].fieldKey  = FWPM_CONDITION_IP_LOCAL_PORT;
    staFilterConditions[nConditionIndex].matchType = FWP_MATCH_EQUAL;
    staFilterConditions[nConditionIndex].conditionValue.type   = FWP_UINT16;
    staFilterConditions[nConditionIndex].conditionValue.uint16 = DNS_PORT;
    nConditionIndex++;
    stDnsFilter.numFilterConditions = nConditionIndex;
    dwResult = FwpmFilterAdd(
        m_hWfpEngineHandle,
        &stDnsFilter,
        NULL,
        &m_nDatagramV6DataFilterID
        );
    if(ERROR_SUCCESS != dwResult)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Error creating filter flow UDP IPv6 DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        m_nDatagramsV6CalloutId = 0;
        goto abortWfpTransaction;
    }
    dwResult = FwpmTransactionCommit(m_hWfpEngineHandle);
    ASSERT(ERROR_SUCCESS == dwResult);
    m_hDnsFilterDevice = CreateFileW(
        DNSFILTER_DOS_NAME,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
        );
    if(INVALID_HANDLE_VALUE == m_hDnsFilterDevice)
    {
        dwResult = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Failed to open device filter DNS packets. Error: %!WINERROR!",
            dwResult
            );
        m_nRetCode = dwResult;
        return FALSE;
    }
    TraceEvents(
        TRACE_LEVEL_INFORMATION,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! DNSFILTER root device file was created."
        );
    {
        PBYTE   pIOBuffer = NULL;
        size_t  nIOBufferSize = sizeof(ADDRESSES_DESCRIPTOR);
        int     i;
        int     nArrayPossition = sizeof(ADDRESSES_DESCRIPTOR);
        PADDRESSES_DESCRIPTOR   pAddressesDescriptor = NULL;
        nIOBufferSize += theApp.m_nPublicIpCount*sizeof(UINT32);
        nIOBufferSize += theApp.m_nPublicRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
        nIOBufferSize += theApp.m_nPublicSubnetCount*sizeof(IP4SUBNET_STRUCT);
        nIOBufferSize += theApp.m_nPrivateIpCount*sizeof(UINT32);
        nIOBufferSize += theApp.m_nPrivateRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
        nIOBufferSize += theApp.m_nPrivateSubnetCount*sizeof(IP4SUBNET_STRUCT);
        nIOBufferSize += theApp.m_nPublicIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
        nIOBufferSize += theApp.m_nPrivateIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
        pIOBuffer = new BYTE[nIOBufferSize];
        ASSERT(NULL != pIOBuffer);
        pAddressesDescriptor = (PADDRESSES_DESCRIPTOR)pIOBuffer;
        pAddressesDescriptor->m_nPublicIpCount = theApp.m_nPublicIpCount;
        pAddressesDescriptor->m_pPublicIpArray = (UINT32*)&pIOBuffer[nArrayPossition];
        for(i=0;i<theApp.m_nPublicIpCount;i++)
        {
            pAddressesDescriptor->m_pPublicIpArray[i] = theApp.m_pPublicIpArray[i];
        }
        nArrayPossition += theApp.m_nPublicIpCount*sizeof(UINT32);
        pAddressesDescriptor->m_nPrivateIpCount = theApp.m_nPrivateIpCount;
        pAddressesDescriptor->m_pPrivateIpArray = (UINT32*)&pIOBuffer[nArrayPossition];
        for(i=0;i<theApp.m_nPrivateIpCount;i++)
        {
            pAddressesDescriptor->m_pPrivateIpArray[i] = theApp.m_pPrivateIpArray[i];
        }
        nArrayPossition += theApp.m_nPrivateIpCount*sizeof(UINT32);
        pAddressesDescriptor->m_nPublicRangeCount = theApp.m_nPublicRangeCount;
        pAddressesDescriptor->m_pPublicRangeArray = (PIP4ADDR_RANGE_STRUCT)&pIOBuffer[nArrayPossition];
        for(i=0;i<theApp.m_nPublicRangeCount;i++)
        {
            pAddressesDescriptor->m_pPublicRangeArray[i]._ulLowValue  = theApp.m_pPublicRangeArray[i].valueLow.uint32;
            pAddressesDescriptor->m_pPublicRangeArray[i]._ulHighValue = theApp.m_pPublicRangeArray[i].valueHigh.uint32;
        }
        nArrayPossition += theApp.m_nPublicRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
        pAddressesDescriptor->m_nPrivateRangeCount = theApp.m_nPrivateRangeCount;
        pAddressesDescriptor->m_pPrivateRangeArray = (PIP4ADDR_RANGE_STRUCT)&pIOBuffer[nArrayPossition];
        for(i=0;i<theApp.m_nPrivateRangeCount;i++)
        {
            pAddressesDescriptor->m_pPrivateRangeArray[i]._ulLowValue  = theApp.m_pPrivateRangeArray[i].valueLow.uint32;
            pAddressesDescriptor->m_pPrivateRangeArray[i]._ulHighValue = theApp.m_pPrivateRangeArray[i].valueHigh.uint32;
        }
        nArrayPossition += theApp.m_nPrivateRangeCount*sizeof(IP4ADDR_RANGE_STRUCT);
        pAddressesDescriptor->m_nPublicSubnetCount = theApp.m_nPublicSubnetCount;
        pAddressesDescriptor->m_pPublicSubnetArray = (PIP4SUBNET_STRUCT)&pIOBuffer[nArrayPossition];
        for(i=0;i<theApp.m_nPublicSubnetCount;i++)
        {
            pAddressesDescriptor->m_pPublicSubnetArray[i]._ulIpAddr = theApp.m_pPublicSubnetArray[i].addr;
            pAddressesDescriptor->m_pPublicSubnetArray[i]._ulIpMask = theApp.m_pPublicSubnetArray[i].mask;
        }
        nArrayPossition += theApp.m_nPublicSubnetCount*sizeof(IP4SUBNET_STRUCT);
        pAddressesDescriptor->m_nPrivateSubnetCount = theApp.m_nPrivateSubnetCount;
        pAddressesDescriptor->m_pPrivateSubnetArray = (PIP4SUBNET_STRUCT)&pIOBuffer[nArrayPossition];
        for(i=0;i<theApp.m_nPrivateSubnetCount;i++)
        {
            pAddressesDescriptor->m_pPrivateSubnetArray[i]._ulIpAddr = theApp.m_pPrivateSubnetArray[i].addr;
            pAddressesDescriptor->m_pPrivateSubnetArray[i]._ulIpMask = theApp.m_pPrivateSubnetArray[i].mask;
        }
        nArrayPossition += theApp.m_nPrivateSubnetCount*sizeof(IP4SUBNET_STRUCT);
        pAddressesDescriptor->m_nPublicIPV6AddrCount = theApp.m_nPublicIPV6AddrCount;
        pAddressesDescriptor->m_pPublicIPV6AddrArray = (PIPV6_ADDRESS_STRUCT) &pIOBuffer[nArrayPossition];
        for (i = 0; i<theApp.m_nPublicIPV6AddrCount; i++)
        {
            RtlCopyMemory(
                &pAddressesDescriptor->m_pPublicIPV6AddrArray[i],
                &theApp.m_pPublicIPV6AddrArray[i],
                sizeof(pAddressesDescriptor->m_pPublicIPV6AddrArray[i])
                );
        }
        nArrayPossition += theApp.m_nPublicIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
        pAddressesDescriptor->m_nPrivateIPV6AddrCount = theApp.m_nPrivateIPV6AddrCount;
        pAddressesDescriptor->m_pPrivateIPV6AddrArray = (PIPV6_ADDRESS_STRUCT) &pIOBuffer[nArrayPossition];
        for (i = 0; i<theApp.m_nPrivateIPV6AddrCount; i++)
        {
            RtlCopyMemory(
                &pAddressesDescriptor->m_pPrivateIPV6AddrArray[i],
                &theApp.m_pPrivateIPV6AddrArray[i],
                sizeof(pAddressesDescriptor->m_pPrivateIPV6AddrArray[i])
                );
        }
        nArrayPossition += theApp.m_nPrivateIPV6AddrCount*sizeof(IPV6_ADDRESS_STRUCT);
        bRet = DeviceIoControl(
            m_hDnsFilterDevice,
            DNSFILTER_IOCTL_INIT_FILTERS,
            pIOBuffer,
            (DWORD)nIOBufferSize,
            NULL,
            0,
            &dwResult,
            NULL
            );
        if(!bRet)
        {
            dwResult = ::GetLastError();
            TraceEvents(
                TRACE_LEVEL_ERROR,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Failed to start the driver initialization operations. Error: %!WINERROR!",
                dwResult
                );
            m_nRetCode = dwResult;
            delete pIOBuffer;
            return FALSE;
        }
        delete pIOBuffer;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! DNSFILTER root device was initializiled."
            );
    }
    bRet = theApp.m_oInitWorkerThreadDone.SetEvent();
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Exit");
    return TRUE;
abortWfpTransaction:
    m_nDatagramV6DataFilterID = 0;
    m_nDatagramDataFilterID = 0;
    m_nStreamV6FilterID = 0;
    m_nStreamFilterID = 0;
    m_nAleFlowEstablishedV6FilterID = 0;
    m_nAleFlowEstablishedFilterID = 0;
    m_nDatagramsV6CalloutId = 0;
    m_nDatagramsCalloutId = 0;
    m_nStreamV6CalloutId = 0;
    m_nStreamCalloutId = 0;
    m_nAleFlowEstablishedV6CalloutId = 0;
    m_nAleFlowEstablishedCalloutId = 0;
    dwResult = FwpmTransactionAbort(m_hWfpEngineHandle);
    //ASSERT(ERROR_SUCCESS == dwResult);
    TraceEvents(
        TRACE_LEVEL_ERROR,
        FILE_TRACE,
        "%!FUNC! line:%!LINE! %!LEVEL! Exit with Abort WfpTransaction"
        );
    return FALSE;
}

int CMainWorkerThread::ExitInstance()
{
    DWORD                   dwResult;
    BOOL                    bRet;
    DWORD                   dwWaitState;
    HANDLE                  hElaborateThread;
    DWORD                   dwThreadHandle, dwThreadID;
    CIp4ElaborateThread*    pElaborateThread;
    SC_HANDLE               schSCManager;
    SC_HANDLE               schService;
    DWORD                   cbBytesNeeded;
    SERVICE_STATUS          stServiceStatus;
    SERVICE_STATUS_PROCESS  stServiceProcessStatus;
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Enter");
    bRet = theApp.m_oWorkerThreadLock.Lock();
    m_bOnDelete = true;
    bRet = m_oDnsSessionsListLock.Lock();
    while (!m_oElaboratedThreadsList.IsEmpty())
    {
        pElaborateThread = m_oElaboratedThreadsList.RemoveTail();
        pElaborateThread->m_pElaboratedListPositon = NULL;
        dwThreadHandle = (DWORD) pElaborateThread->m_hThread;
        dwThreadID = pElaborateThread->m_nThreadID;
        bRet = DuplicateHandle(
            GetCurrentProcess(),
            *pElaborateThread,
            GetCurrentProcess(),
            &hElaborateThread,
            0,
            FALSE,
            DUPLICATE_SAME_ACCESS
            );
        ASSERT(bRet);
        bRet = pElaborateThread->PostThreadMessage(
            WM_QUIT,
            0,
            0
            );
        dwWaitState = SignalObjectAndWait(
            m_oDnsSessionsListLock,
            pElaborateThread->m_oThreadExitDone,
            INFINITE,
            FALSE
            );
        bRet = m_oDnsSessionsListLock.Lock();
        ASSERT(WAIT_OBJECT_0 == dwWaitState);
        dwWaitState = ::WaitForSingleObject(
            hElaborateThread,
            INFINITE
            );
        ASSERT(WAIT_OBJECT_0 == dwWaitState);
        bRet = ::CloseHandle(hElaborateThread);
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Elaborated thread done. hThread = %08X, nThreadID = %08X.",
            dwThreadHandle,
            dwThreadID
            );
    }
    while (!m_oAllocatedThreadsList.IsEmpty())
    {
        pElaborateThread = m_oAllocatedThreadsList.RemoveTail();
        pElaborateThread->m_pAllocatedListPosition = NULL;
        dwThreadHandle = (DWORD) pElaborateThread->m_hThread;
        dwThreadID = pElaborateThread->m_nThreadID;
        bRet = DuplicateHandle(
            GetCurrentProcess(),
            *pElaborateThread,
            GetCurrentProcess(),
            &hElaborateThread,
            0,
            FALSE,
            DUPLICATE_SAME_ACCESS
            );
        ASSERT(bRet);
        bRet = pElaborateThread->PostThreadMessage(
            WM_QUIT,
            0,
            0
            );
        dwWaitState = SignalObjectAndWait(
            m_oDnsSessionsListLock,
            pElaborateThread->m_oThreadExitDone,
            INFINITE,
            FALSE
            );
        bRet = m_oDnsSessionsListLock.Lock();
        ASSERT(WAIT_OBJECT_0 == dwWaitState);
        dwWaitState = ::WaitForSingleObject(
            hElaborateThread,
            INFINITE
            );
        ASSERT(WAIT_OBJECT_0 == dwWaitState);
        bRet = ::CloseHandle(hElaborateThread);
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Allocated thread done. hThread = %08X, nThreadID = %08X.",
            dwThreadHandle,
            dwThreadID
            );
    }
    bRet = m_oDnsSessionsListLock.Unlock();
    if (INVALID_HANDLE_VALUE != m_hDnsFilterDevice)
    {
        bRet = ::CloseHandle(m_hDnsFilterDevice);
        m_hDnsFilterDevice = INVALID_HANDLE_VALUE;
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! DNSFILTER root device was closed."
            );
    }
    if (0 != m_nDatagramV6DataFilterID)
    {
        dwResult = FwpmFilterDeleteById(
            m_hWfpEngineHandle,
            m_nDatagramV6DataFilterID
            );
        m_nDatagramV6DataFilterID = 0;
    }
    if (0 != m_nDatagramDataFilterID)
    {
        dwResult = FwpmFilterDeleteById(
            m_hWfpEngineHandle,
            m_nDatagramDataFilterID
            );
        m_nDatagramDataFilterID = 0;
    }
    if (0 != m_nStreamV6FilterID)
    {
        dwResult = FwpmFilterDeleteById(
            m_hWfpEngineHandle,
            m_nStreamV6FilterID
            );
        m_nStreamV6FilterID = 0;
    }
    if (0 != m_nStreamFilterID)
    {
        dwResult = FwpmFilterDeleteById(
            m_hWfpEngineHandle,
            m_nStreamFilterID
            );
        m_nStreamFilterID = 0;
    }
    if (0 != m_nAleFlowEstablishedV6FilterID)
    {
        dwResult = FwpmFilterDeleteById(
            m_hWfpEngineHandle,
            m_nAleFlowEstablishedV6FilterID
            );
        m_nAleFlowEstablishedV6FilterID = 0;
    }
    if (0 != m_nAleFlowEstablishedFilterID)
    {
        dwResult = FwpmFilterDeleteById(
            m_hWfpEngineHandle,
            m_nAleFlowEstablishedFilterID
            );
        m_nAleFlowEstablishedFilterID = 0;
    }
    if (0 != m_nDatagramsV6CalloutId)
    {
        dwResult = FwpmCalloutDeleteById(
            m_hWfpEngineHandle,
            m_nDatagramsV6CalloutId
            );
        m_nDatagramsV6CalloutId = 0;
    }
    if (0 != m_nDatagramsCalloutId)
    {
        dwResult = FwpmCalloutDeleteById(
            m_hWfpEngineHandle,
            m_nDatagramsCalloutId
            );
        m_nDatagramsCalloutId = 0;
    }
    if (0 != m_nStreamV6CalloutId)
    {
        dwResult = FwpmCalloutDeleteById(
            m_hWfpEngineHandle,
            m_nStreamV6CalloutId
            );
        m_nStreamV6CalloutId = 0;
    }
    if (0 != m_nStreamCalloutId)
    {
        dwResult = FwpmCalloutDeleteById(
            m_hWfpEngineHandle,
            m_nStreamCalloutId
            );
        m_nStreamCalloutId = 0;
    }
    if (0 != m_nAleFlowEstablishedV6CalloutId)
    {
        dwResult = FwpmCalloutDeleteById(
            m_hWfpEngineHandle,
            m_nAleFlowEstablishedV6CalloutId
            );
        m_nAleFlowEstablishedV6CalloutId = 0;
    }
    if (0 != m_nAleFlowEstablishedCalloutId)
    {
        dwResult = FwpmCalloutDeleteById(
            m_hWfpEngineHandle,
            m_nAleFlowEstablishedCalloutId
            );
        m_nAleFlowEstablishedCalloutId = 0;
    }
    if (INVALID_HANDLE_VALUE != m_hWfpEngineHandle)
    {
        dwResult = FwpmSubLayerDeleteByKey0(
            m_hWfpEngineHandle,
            &FWPM_SUBLAYER_UNIVERSAL
            );
        dwResult = FwpmEngineClose0(
            m_hWfpEngineHandle
            );
        m_hWfpEngineHandle = INVALID_HANDLE_VALUE;
    }
    if (NULL != m_pDnsServerAppID)
    {
        FwpmFreeMemory0((void**) &m_pDnsServerAppID);
        m_pDnsServerAppID = NULL;
    }
    schSCManager = OpenSCManager(
        NULL,                 // local machine
        NULL,                 // ServicesActive database
        SC_MANAGER_ALL_ACCESS // full access rights
        );
    if (NULL == schSCManager)
    {
        m_nRetCode = ::GetLastError();
        TraceEvents(
            TRACE_LEVEL_ERROR,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Function \"OpenSCManager\" failed with error %!WINERROR!",
            m_nRetCode
            );
        bRet = theApp.m_oMainWorkerThreadDoneEvent.SetEvent();
        bRet = theApp.m_oWorkerThreadLock.Unlock();
        CWinThread::ExitInstance();
        return m_nRetCode;
    }
    schService = OpenService(
        schSCManager,
        _T("PublicDNSPluginService"),
        SERVICE_ALL_ACCESS
        );
    if (NULL == schService)
    {
        bRet = CloseServiceHandle(schSCManager);
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (the \"PublicDNSPluginService\" service has not been installed)"
            );
        bRet = CloseServiceHandle(schSCManager);
        bRet = theApp.m_oMainWorkerThreadDoneEvent.SetEvent();
        bRet = theApp.m_oWorkerThreadLock.Unlock();
        CWinThread::ExitInstance();
        return m_nRetCode;
    }
    RtlZeroMemory(
        &stServiceProcessStatus,
        sizeof(stServiceProcessStatus)
        );
    RtlZeroMemory(
        &stServiceStatus,
        sizeof(stServiceStatus)
        );
    bRet = QueryServiceStatusEx(
        schService,
        SC_STATUS_PROCESS_INFO,
        (LPBYTE) &stServiceProcessStatus,
        sizeof(stServiceProcessStatus),
        &cbBytesNeeded
        );
    if (SERVICE_RUNNING != stServiceProcessStatus.dwCurrentState)
    {
        TraceEvents(
            TRACE_LEVEL_INFORMATION,
            FILE_TRACE,
            "%!FUNC! line:%!LINE! %!LEVEL! Exit (the \"PublicDNSPluginService\" service not in RUNNING state)"
            );
    }
    bRet = CloseServiceHandle(schService);
    bRet = CloseServiceHandle(schSCManager);
    bRet = theApp.m_oMainWorkerThreadDoneEvent.SetEvent();
    bRet = theApp.m_oWorkerThreadLock.Unlock();
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Exit");
    CWinThread::ExitInstance();
    return m_nRetCode;
}

BEGIN_MESSAGE_MAP(CMainWorkerThread, CWinThread)
END_MESSAGE_MAP()


// CMainWorkerThread message handlers


BOOL CMainWorkerThread::OnIdle(LONG lCount)
{
    DWORD   dwWaitState;
    BOOL    bRet;
    lCount;
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Enter");
    if (!m_bServiceTest)
    {
        HANDLE  ahWaitedHandles[] = {
            m_oThreadsSemaphore
        };
        dwWaitState = MsgWaitForMultipleObjects(
            1,
            ahWaitedHandles,
            FALSE,
            TEST_PERIOD_TIME,
            QS_POSTMESSAGE
            );
        switch(dwWaitState)
        {
        case WAIT_OBJECT_0:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! The semaphore \"m_oThreadsSemaphore\" has been signaled"
                );
            {
                CIp4ElaborateThread*    pIp4ElaborateThread;
                pIp4ElaborateThread = new CIp4ElaborateThread;
                if(NULL == pIp4ElaborateThread)
                {
                    m_nRetCode = ERROR_NOT_ENOUGH_MEMORY;
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Error allocating memory for an object flow DNS-packets"
                        );
                    bRet = PostThreadMessage(
                        WM_QUIT,
                        m_nRetCode,
                        0
                        );
                    return FALSE;
                }
                pIp4ElaborateThread->m_pOwner = this;
                bRet = pIp4ElaborateThread->CreateThread( );
                if(!bRet)
                {
                    CString sMsg;
                    try
                    {
                        delete pIp4ElaborateThread;
                    }
                    catch(...)
                    {
                    }
                    m_nRetCode = -1;
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Error creating pending thread for DNS-packets flow"
                        );
                    bRet = PostThreadMessage(
                        WM_QUIT,
                        m_nRetCode,
                        0
                        );
                    return FALSE;
                }
                {
                    HANDLE  haWaitHandles[] = {
                        pIp4ElaborateThread->m_oThreadInitDone,
                        *pIp4ElaborateThread
                    };
                    DWORD   dwWaitState;
                    dwWaitState = ::WaitForMultipleObjects(
                        2,
                        haWaitHandles,
                        FALSE,
                        INFINITE
                        );
                    if(WAIT_OBJECT_0 != dwWaitState)
                    {
                        m_nRetCode = -1;
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            FILE_TRACE,
                            "%!FUNC! line:%!LINE! %!LEVEL! Error initialization of thread for processing DNS packets"
                            );
                        bRet = PostThreadMessage(
                            WM_QUIT,
                            m_nRetCode,
                            0
                            );
                        return FALSE;
                    }
                }
            }
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! DNS packets elaborate thread was created successfull."
                );
            break;
        case WAIT_OBJECT_0+1:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! New message in the message queue of the DNS packets handler"
                );
            {
                MSG     stMsg;
                bRet = PeekMessage(
                    &stMsg,
                    (HWND)NULL,
                    NULL,
                    NULL,
                    PM_NOREMOVE
                    );
                if(bRet && (WM_QUIT == stMsg.message))
                {
                    TraceEvents(
                        TRACE_LEVEL_INFORMATION,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Exit (bRet && (WM_QUIT == stMsg.message))"
                        );
                    return FALSE;
                }
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exit (!(bRet && (WM_QUIT == stMsg.message)))"
                    );
            }
            return TRUE;
        case WAIT_TIMEOUT:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! Events wait timed out. Handler of DNS packets will be tested."
                );
            {
                BYTE    baInStackBuffer[4096];
                DWORD   dwBufferSize = sizeof(baInStackBuffer);
                int     nSockError;
                m_wXid++;
                RtlZeroMemory(
                    baInStackBuffer,
                    sizeof(baInStackBuffer)
                    );
                bRet = DnsWriteQuestionToBuffer_W(
                    (PDNS_MESSAGE_BUFFER)baInStackBuffer,
                    &dwBufferSize,
                    theApp.m_sDnsZoneName,
                    DNS_TYPE_SOA,
                    m_wXid,
                    FALSE
                    );
                ASSERT(bRet && (dwBufferSize <= 512));
                RtlCopyMemory(
                    &m_stUdpDNSQueryMessage.m_stMessageBufferPrefix,
                    baInStackBuffer,
                    dwBufferSize
                    );
                dwBufferSize = m_oUdpLocalSocket.SendTo(
                    &m_stUdpDNSQueryMessage,
                    dwBufferSize,
                    &m_stLocalDnsServerAddress,
                    sizeof(m_stLocalDnsServerAddress)
                    );
                if(SOCKET_ERROR == dwBufferSize)
                {
                    nSockError = CAsyncSocket::GetLastError();
                    m_nRetCode = nSockError;
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Transmission error DNS query. Error is %!WINERROR!",
                        nSockError
                        );
                    bRet = PostThreadMessage(
                        WM_QUIT,
                        m_nRetCode,
                        0
                        );
                    return FALSE;
                }
                m_bServiceTest = true;
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exit (DNS request has been sent)"
                    );
            }
            return TRUE;
        }
    }
    else
    {
        HANDLE  ahWaitedHandles[] = {
            m_oThreadsSemaphore,
            m_oDnsResponceEvent
        };
        dwWaitState = MsgWaitForMultipleObjects(
            2,
            ahWaitedHandles,
            FALSE,
            TEST_PERIOD_TIME,
            QS_POSTMESSAGE
            );
        switch(dwWaitState)
        {
        case WAIT_OBJECT_0:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! The semaphore \"m_oThreadsSemaphore\" has been signaled"
                );
            {
                CIp4ElaborateThread*    pIp4ElaborateThread;
                pIp4ElaborateThread = new CIp4ElaborateThread;
                if(NULL == pIp4ElaborateThread)
                {
                    m_nRetCode = ERROR_NOT_ENOUGH_MEMORY;
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Error allocating memory for an object flow DNS-packets"
                        );
                    bRet = PostThreadMessage(
                        WM_QUIT,
                        m_nRetCode,
                        0
                        );
                    return FALSE;
                }
                pIp4ElaborateThread->m_pOwner = this;
                bRet = pIp4ElaborateThread->CreateThread( );
                if(!bRet)
                {
                    try
                    {
                        delete pIp4ElaborateThread;
                    }
                    catch(...)
                    {
                    }
                    m_nRetCode = -1;
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Error creating pending thread for DNS-packets flow"
                        );
                    bRet = PostThreadMessage(
                        WM_QUIT,
                        m_nRetCode,
                        0
                        );
                    return FALSE;
                }
                {
                    HANDLE  haWaitHandles[] = {
                        pIp4ElaborateThread->m_oThreadInitDone,
                        *pIp4ElaborateThread
                    };
                    DWORD   dwWaitState;
                    dwWaitState = ::WaitForMultipleObjects(
                        2,
                        haWaitHandles,
                        FALSE,
                        INFINITE
                        );
                    if(WAIT_OBJECT_0 != dwWaitState)
                    {
                        m_nRetCode = -1;
                        TraceEvents(
                            TRACE_LEVEL_ERROR,
                            FILE_TRACE,
                            "%!FUNC! line:%!LINE! %!LEVEL! Error initialization of thread for processing DNS packets"
                            );
                        bRet = PostThreadMessage(
                            WM_QUIT,
                            m_nRetCode,
                            0
                            );
                        return FALSE;
                    }
                }
            }
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! DNS packets elaborate thread was created successfull."
                );
            break;
        case WAIT_OBJECT_0+1:
            TraceEvents(
                TRACE_LEVEL_VERBOSE,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! The event \"m_oDnsResponceEvent\" has been signaled"
                );
            {
                int     nReceivedByte;
                WORD    wDnsLength;
                bool    bMessageParsed = false;
                CDnsMessageWB*  pDnsMessage = NULL;
                PDNS_HEADER     pDnsHeader = (PDNS_HEADER)m_baUdpBuffer;
                CDnsResorceRecord*  pCurrentRR;
                CDnsResorceRecord*  pNextRR;
                CDnsResorceRecord*  pOldDnsZoneRecord;
                bRet = m_oDnsResponceEvent.ResetEvent();
                m_bServiceTest = false;
                nReceivedByte = m_oUdpLocalSocket.Receive(
                    m_baUdpBuffer,
                    sizeof(m_baUdpBuffer)
                    );
                if(0 == nReceivedByte)
                {
                    m_nRetCode = -1;
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! You are attempting to access data in a closed UDP connection. The service will be restarted."
                        );
                    theApp.RestartService();
                    return FALSE;
                }
                if(SOCKET_ERROR == nReceivedByte)
                {
                    int     nSockError = CAsyncSocket::GetLastError();
                    m_nRetCode = nSockError;
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Error receiving DNS response. Error is %!WINERROR!. The service will be restarted.",
                        nSockError
                        );
                    theApp.RestartService();
                    return FALSE;
                }
                if(pDnsHeader->Truncation)
                {
                    m_nRetCode = -1;
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Response to the SOA message should not be truncated. The service will be restarted."
                        );
                    theApp.RestartService();
                    return FALSE;
                }
                wDnsLength = (WORD)nReceivedByte;
                pDnsMessage = new CDnsMessageWB;
                ASSERT(NULL != pDnsMessage);
                bMessageParsed = pDnsMessage->ParseDnsMessageBuffer(
                    (PDNS_MESSAGE_BUFFER)m_baUdpBuffer,
                    wDnsLength
                    );
                if(!bMessageParsed)
                {
                    delete pDnsMessage;
                    m_nRetCode = -1;
                    TraceEvents(
                        TRACE_LEVEL_ERROR,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! DNS response parse error. The service will be restarted."
                        );
                    theApp.RestartService();
                    return FALSE;
                }
                pCurrentRR = pDnsMessage->m_pFirstDnsRRecord;
                while(NULL != pCurrentRR)
                {
                    pNextRR = pCurrentRR->m_pNext;
                    if(DNS_TYPE_SOA == pCurrentRR->m_wType)
                    {
                        break;
                    }
                    pCurrentRR = pNextRR;
                }
                ASSERT(NULL != pCurrentRR);
                pOldDnsZoneRecord = theApp.m_pDnsZoneRecord;
                theApp.m_pDnsZoneRecord = new CDnsResorceRecord(*pCurrentRR);
                delete pOldDnsZoneRecord;
                delete pDnsMessage;
            }
            break;
        case WAIT_OBJECT_0+2:
            TraceEvents(
                TRACE_LEVEL_INFORMATION,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! New message in the message queue of the DNS packets handler"
                );
            {
                MSG     stMsg;
                bRet = PeekMessage(
                    &stMsg,
                    (HWND)NULL,
                    NULL,
                    NULL,
                    PM_NOREMOVE
                    );
                if (bRet && (WM_QUIT == stMsg.message))
                {
                    TraceEvents(
                        TRACE_LEVEL_INFORMATION,
                        FILE_TRACE,
                        "%!FUNC! line:%!LINE! %!LEVEL! Exit (bRet && (WM_QUIT == stMsg.message))"
                        );
                    return FALSE;
                }
                TraceEvents(
                    TRACE_LEVEL_INFORMATION,
                    FILE_TRACE,
                    "%!FUNC! line:%!LINE! %!LEVEL! Exit (!(bRet && (WM_QUIT == stMsg.message)))"
                    );
            }
            return TRUE;
        case WAIT_TIMEOUT:
            TraceEvents(
                TRACE_LEVEL_WARNING,
                FILE_TRACE,
                "%!FUNC! line:%!LINE! %!LEVEL! DNS response timeout. The service will be restarted."
                );
            m_nRetCode = WAIT_TIMEOUT;
            m_bServiceTest = false;
            theApp.RestartService();
            return FALSE;
        }
    }
    TraceEvents(TRACE_LEVEL_INFORMATION, FILE_TRACE, "%!FUNC! line:%!LINE! %!LEVEL! Exit");
    return TRUE;
}
